
    def GetDeltaByNote(self, note, offset = 0):

        if(note + offset < 42 or note + offset > 114):
            raise ValueError("")
        
        nnote = note - 42 + offset
        x = int(nnote / 30)
        y = nnote - x * 30
        return [3 * x, 0, 2 * y]
    
    """
@param pos Absolute coordinate of the keyboard
    """
    def GenerateKeyboard(self, pos):
        ret = []
        self.pos = pos
        for i in range(42, 115):
            delta = self.GetDeltaByNote(i)
            npos = [pos[0] + delta[0] + 1, pos[1], pos[2] + delta[2]]
            note = midiParser.note(i, 0, 0).toMCNote();
            cmd = self.COMMANDEXECUTE.format(soundname = self.INSTRUMENT[note[1]], tune = self.PITCH[note[0]])
            cmd = self.COMMANDPLACEREPEATCB.format(pos = npos, facing = "east", cmd = cmd)
            ret.append(cmd)

            npos = [pos[0] + delta[0], pos[1], pos[2] + delta[2]]
            cmd = self.COMMANDPLACEREPEATER.format(pos = npos, facing = "west", delay = 1)
            ret.append(cmd)
        return ret

    def GetCommandActivate(self, note):
        delta = self.GetDeltaByNote(note)
        npos = [self.pos[0] + delta[0], self.pos[1] + delta[1], self.pos[2] + delta[2]]
        return 'setblock {pos[0]} {pos[1]} {pos[2]} minecraft:repeater[facing=west, delay=1, powered=true] replace'.format(pos = npos);
    
    def GetCommandDeactivate(self, note):
        delta = self.GetDeltaByNote(note)
        npos = [self.pos[0] + delta[0], self.pos[1] + delta[1], self.pos[2] + delta[2]]
        return 'setblock {pos[0]} {pos[1]} {pos[2]} minecraft:repeater[facing=west, delay=1, powered=false] replace'.format(pos = npos);

    def GenerateTimeline(self, fileName, offset = 0):
        with mido.MidiFile(fileName) as midifile:
            for i, track in enumerate(midifile.tracks):
                print("Track {}:{}".format(i, track))
            tps = 0
            queue = []
            for msg in midifile.tracks[0]:
                #print(msg)
                if msg.is_meta:
                    if msg.dict()["type"] == "set_tempo":
                       tps = mido.tick2second(1, midifile.ticks_per_beat, msg.dict()["tempo"])
            for msg in midifile.tracks[1]:
                if not msg.is_meta:
                    dic = msg.dict()
                    if dic["type"] == "note_on" and dic["velocity"] > 0:
                        queue.append((1, dic["note"], dic["time"] * tps))
                    elif dic["type"] == "note_off" or (dic["type"] == "note_on" and dic["velocity"] == 0):
                        queue.append((0, dic["note"], dic["time"] * tps))

            currentDelta = [0, 0, 0]
            facing = "east"

            retVal = []
            cnt = 0
            for i in queue:

                if(i[1] + offset < 42 or i[1] + offset > 114):
                    continue
                
                deltaTime = i[2]
                if(deltaTime < 0):
                    raise ValueError("List of notes should be sorted by starting time")
                if(deltaTime != 0):
                    ret = mg.GenerateDelayArray(facing, int(ConvertTick(deltaTime)), ConvertDelta(currentDelta))
                    currentDelta = MoveForward(currentDelta, facing, ret[1])
                    for k in ret[0]:
                        retVal.append(k)
                if(deltaTime == 0):
                    ret = mg.GenerateRedstoneWire(ConvertDelta(currentDelta))
                    currentDelta = MoveForward(currentDelta, facing, 1)
                    retVal.append(ret)
                
                cmd = ""
                if(i[0] == 0):
                    cmd = self.GetCommandDeactivate(i[1] + offset)
                if(i[0] == 1):
                    cmd = self.GetCommandActivate(i[1] + offset)
                cmd = self.COMMANDPLACENORMALCB.format(pos = ConvertDelta(), facing = facing, cmd = cmd)
                cmd = self.COMMANDREMOTEPLACE.format(pos = ConvertDelta(currentDelta), cmd = cmd)
                retVal.append(cmd)
                cmd = mg.GenerateRedstoneWire(ConvertDelta([currentDelta[0], currentDelta[1] + 1, currentDelta[2]]))
                retVal.append(cmd)
                currentDelta = MoveForward(currentDelta, facing, 1)

                cnt = cnt + 1
                if(cnt == 20):
                    cnt = 0
                    ret = mg.GenerateRedstoneWire(ConvertDelta(currentDelta))
                    retVal.append(ret)
                    currentDelta[2] = currentDelta[2] + 1
                    ret = mg.GenerateRedstoneWire(ConvertDelta(currentDelta))
                    retVal.append(ret)
                    currentDelta[2] = currentDelta[2] + 1
                    ret = mg.GenerateRedstoneWire(ConvertDelta(currentDelta))
                    retVal.append(ret)
                    
                    facing = GetOppositeFacing(facing)
                    currentDelta = MoveForward(currentDelta, facing, 1)
            return retVal
